package data

import (
	"fmt"
	"os"
	"strings"
	"sync"
	"yunj/pkg/global"
	"yunj/pkg/yunj/app/enum/admin/authenum"
	"yunj/pkg/yunj/app/enum/admin/isdemoenum"
	"yunj/pkg/yunj/app/enum/stateenum"
	"yunj/pkg/yunj/app/model"
	"yunj/pkg/yunj/core/db"
	"yunj/pkg/yunj/util"

	"gorm.io/gorm"
)

var dataAuthMutex sync.RWMutex

type auth struct {
	// 所有权限
	allAuths      []*model.AdminAuth          // 所有权限
	allAuthMap    map[string]*model.AdminAuth // key:auth
	allAuthKeys   []string                    // 按sort排序
	allAuthKeyIdx map[string]int              // key:index

	// 菜单权限
	allMenuAuths      []*model.AdminAuth          // 所有菜单权限
	allMenuAuthMap    map[string]*model.AdminAuth // key:auth
	allMenuAuthKeys   []string                    // 按sort排序
	allMenuAuthKeyIdx map[string]int              // key:index
}

var Auth = &auth{}

// 获取所有权限
func (s *auth) GetAllAuths() []*model.AdminAuth {
	dataAuthMutex.RLock()         // 获取读锁
	defer dataAuthMutex.RUnlock() // 释放读锁
	return s.allAuths
}

// 获取所有权限map
func (s *auth) GetAllAuthMap() map[string]*model.AdminAuth {
	dataAuthMutex.RLock()         // 获取读锁
	defer dataAuthMutex.RUnlock() // 释放读锁
	return s.allAuthMap
}

// 获取所有权限map
func (s *auth) GetAllAuthKeys() []string {
	dataAuthMutex.RLock()         // 获取读锁
	defer dataAuthMutex.RUnlock() // 释放读锁
	return s.allAuthKeys
}

// 根据key获取权限
func (s *auth) GetAuthByKey(key string) (auth *model.AdminAuth) {
	dataAuthMutex.RLock()         // 获取读锁
	defer dataAuthMutex.RUnlock() // 释放读锁
	if _auth, exists := s.allAuthMap[key]; exists && _auth != nil {
		auth = _auth
	}
	return
}

// 准备所有权限数据
func (s *auth) prepareAllAuths() (err error) {
	var auths []*model.AdminAuth
	if global.Config.App.Admin.UseDemo {
		if err = global.MySQL.Where("state = ?", stateenum.NORMAL).Order("sort asc").Find(&auths).Error; err != nil {
			err = fmt.Errorf("后台所有状态正常权限数据获取异常！%v", err)
			return
		}
	} else {
		if err = global.MySQL.Where("state = ? and is_demo = ?", stateenum.NORMAL, isdemoenum.NO).Order("sort asc").Find(&auths).Error; err != nil {
			err = fmt.Errorf("后台所有状态正常权限数据,不含demo数据获取异常！%v", err)
			return
		}
	}

	authMap := map[string]*model.AdminAuth{}
	authKeys := make([]string, 0, len(auths))
	authKeyIdx := map[string]int{}
	for idx, auth := range auths {
		authMap[auth.Key] = auth
		authKeys = append(authKeys, auth.Key)
		authKeyIdx[auth.Key] = idx
		auth.InitSubAuths(auths)
	}
	// 附加子权限
	s.allAuths = auths
	s.allAuthMap = authMap
	s.allAuthKeys = authKeys
	s.allAuthKeyIdx = authKeyIdx
	return
}

// 准备所有菜单权限数据
func (s *auth) prepareAllMenuAuths() (err error) {
	var auths []*model.AdminAuth
	if global.Config.App.Admin.UseDemo {
		if err = global.MySQL.Where("state = ? and type in ?", stateenum.NORMAL, authenum.GetMenuTypeIntConsts()).Order("sort asc").Find(&auths).Error; err != nil {
			err = fmt.Errorf("后台所有状态正常菜单权限数据获取异常！%v", err)
			return
		}
	} else {
		if err = global.MySQL.Where("state = ? and is_demo = ? and type in ?", stateenum.NORMAL, isdemoenum.NO, authenum.GetMenuTypeIntConsts()).Order("sort asc").Find(&auths).Error; err != nil {
			err = fmt.Errorf("后台所有状态正常菜单权限数据,不含demo数据获取异常！%v", err)
			return
		}
	}

	authMap := map[string]*model.AdminAuth{}
	authKeys := make([]string, 0, len(auths))
	authKeyIdx := map[string]int{}
	for idx, auth := range auths {
		authMap[auth.Key] = auth
		authKeys = append(authKeys, auth.Key)
		authKeyIdx[auth.Key] = idx
		auth.InitSubMenuAuths(auths)
	}
	// 初始化菜单等级
	for _, auth := range auths {
		if auth.ParentKey == "" {
			auth.InitMenuLevel(1)
		}
	}
	s.allMenuAuths = auths
	s.allMenuAuthMap = authMap
	s.allMenuAuthKeys = authKeys
	s.allMenuAuthKeyIdx = authKeyIdx
	return
}

// 获取所有菜单权限
func (s *auth) GetAllMenuAuths() []*model.AdminAuth {
	dataAuthMutex.RLock()         // 获取读锁
	defer dataAuthMutex.RUnlock() // 释放读锁
	return s.allMenuAuths
}

// 处理所有权限数据的完整属性值（补充完整名称等），并清理缓存
func HandleAllAuthFullAttr() (err error) {
	var items []*model.AdminAuth
	if err = global.MySQL.Where("state <> ?", stateenum.DELETED).Find(&items).Error; err != nil {
		err = fmt.Errorf("处理所有权限数据的完整属性值：数据获取异常！%v", err)
		return
	}
	if len(items) > 0 {
		// 获取所有要修改数据
		handleAuthFullAttr(items, map[string]*model.AdminAuth{"": nil})
		dbItems := make([]map[string]interface{}, 0, len(items))
		for _, item := range items {
			dbParentKeys, dbValueErr := item.ParentKeys.Value()
			if dbValueErr != nil {
				err = fmt.Errorf("处理所有权限数据的完整属性值：获取parent_keys异常！%v", dbValueErr)
				return
			}
			dbItems = append(dbItems, map[string]interface{}{
				"key":         item.Key,
				"parent_keys": dbParentKeys,
				"full_name":   item.FullName,
			})
		}
		// 批量修改数据
		res := db.Model(&model.AdminAuth{}).BatchChange(dbItems)
		if res.Error != nil {
			err = fmt.Errorf("处理所有权限数据的完整属性值：批量修改数据失败！%v", res.Error)
			return
		}
	}
	// 清理缓存
	err = AuthPrepareOrReset()
	if err != nil {
		err = fmt.Errorf("处理所有权限数据的完整属性值：清理缓存失败！%v", err)
	}
	return
}

func handleAuthFullAttr(items []*model.AdminAuth, parentsMap map[string]*model.AdminAuth) (err error) {
	newParentsMap := map[string]*model.AdminAuth{}
	for _, item := range items {
		parent, exists := parentsMap[item.ParentKey]
		if !exists {
			continue
		}
		parentKeys := model.StrSliceJson{}
		parentFullName := ""
		if parent != nil {
			parentKeys = append(parent.ParentKeys, parent.Key)
			parentFullName = parent.FullName
		}
		fullName := ""
		if parentFullName != "" {
			fullName = parentFullName + "/"
		}
		item.ParentKeys = parentKeys
		item.FullName = fullName + item.Name
		newParentsMap[item.Key] = item
	}
	if len(newParentsMap) > 0 {
		return handleAuthFullAttr(items, newParentsMap)
	}
	return
}

// 数据准备或重置（等于缓存重置）
func AuthPrepareOrReset() (err error) {
	dataAuthMutex.Lock()         // 获取写锁
	defer dataAuthMutex.Unlock() // 释放写锁
	// 准备所有权限数据
	if err := Auth.prepareAllAuths(); err != nil {
		return err
	}
	// 准备所有菜单权限数据
	if err := Auth.prepareAllMenuAuths(); err != nil {
		return err
	}
	return
}

// 初始化权限数据库
func InitAuthDbData() (err error) {
	authDb := db.Model(&model.AdminAuth{})
	tableName := authDb.GetTableName()
	// 创建数据表
	tableSql := "CREATE TABLE IF NOT EXISTS `" + tableName + "` (" +
		"`key` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '权限唯一标识'," +
		"`parent_key` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '父级key'," +
		"`parent_keys` json DEFAULT NULL COMMENT '所有的父级key'," +
		"`name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '名称'," +
		"`full_name` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '完整名称，父级名称拼接'," +
		"`desc` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '描述'," +
		"`type` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '类型。0常规、11侧边栏菜单、22顶部菜单'," +
		"`icon` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '图标class'," +
		"`request_mode` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '请求方式。0无，11内部路由，22外部地址'," +
		"`page_open` tinyint NOT NULL DEFAULT '0' COMMENT '页面打开方式。0无、11tab子页面、22popup子页面、33new新标签页'," +
		"`request_url` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '请求地址'," +
		"`request_method` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '请求method'," +
		"`request_require_params` json DEFAULT NULL COMMENT '请求必要参数'," +
		"`sort` smallint unsigned NOT NULL DEFAULT '0' COMMENT '排序。默认0'," +
		"`is_system` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否为系统。0否，1是'," +
		"`is_demo` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否为系统demo。0否，1是'," +
		"`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP," +
		"`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP," +
		"`state` tinyint unsigned NOT NULL DEFAULT '11' COMMENT '11 正常 | 22 回收站 | 33 已删除'," +
		"UNIQUE KEY `uk` (`key`) USING BTREE" +
		") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='后台权限表';"
	err = global.MySQL.Exec(tableSql).Error
	if err != nil {
		err = fmt.Errorf("创建数据表%s失败！%v", tableName, err)
		return
	}
	// 判断是否存在数据，不存在则加入系统数据
	var count int64
	err = global.MySQL.Model(&model.AdminAuth{}).Count(&count).Error
	if err != nil {
		err = fmt.Errorf("数据表%s数据是否存在查询异常！%v", tableName, err)
		return
	}
	if count <= 0 {
		var systemAuthDataSqlContent []byte
		systemAuthDataSqlContent, err = os.ReadFile("pkg/yunj/app/data/system_auth_data.sql")
		if err != nil {
			err = fmt.Errorf("数据表%s系统权限数据sql文件不存在！%v", tableName, err)
			return
		}
		// 按分号分割SQL语句
		systemAuthDataStatements := strings.Split(string(systemAuthDataSqlContent), ";")

		// 自定义的其他初始化系统权限数据SQL
		configSystemAuthDataSqlPath := "config/system_auth_data.sql"
		if util.Exists(configSystemAuthDataSqlPath) {
			var configSystemAuthDataSqlContent []byte
			configSystemAuthDataSqlContent, err = os.ReadFile(configSystemAuthDataSqlPath)
			if err != nil {
				err = fmt.Errorf("数据表%s系统权限数据sql文件不存在！%v", tableName, err)
				return
			}
			// 按分号分割SQL语句
			systemAuthDataStatements = append(systemAuthDataStatements, strings.Split(string(configSystemAuthDataSqlContent), ";")...)
		}
		// GORM 的 Exec 默认不支持执行包含多条 SQL 语句的字符串
		err = global.MySQL.Transaction(func(tx *gorm.DB) (err error) {
			// 逐条执行SQL
			for _, stmt := range systemAuthDataStatements {
				// 去除首尾空白
				stmt = strings.TrimSpace(stmt)
				// 跳过空行和注释
				if stmt == "" || strings.HasPrefix(stmt, "--") || strings.HasPrefix(stmt, "/*") {
					continue
				}
				// 执行SQL
				if err = tx.Exec(stmt).Error; err != nil {
					tx.Rollback() // 回滚事务
					err = fmt.Errorf("执行SQL失败：%v\n语句：%s", err, stmt)
					return
				}
			}
			return nil
		})
		if err != nil {
			err = fmt.Errorf("数据表%s系统insert数据异常！%v", tableName, err)
			return
		}
	}
	return
}
